/**
 * @class 天文学描述实体
 * @author DuskyHuang 圣子
 * @description 如黄道面等。
 */

import { _decorator, Component, Node, MeshRenderer, renderer, easing, Vec3, resources, Texture2D } from 'cc';
import { Res } from 'const';
import { planetary, timer } from 'proxies';
import { Dsm } from '../solar/dsm';
const { ccclass, property } = _decorator;

@ccclass('cocosmos.astronomical')
export class Astronomical extends Component {

    @property(MeshRenderer) public lunarPlane: MeshRenderer = null;
    @property(MeshRenderer) public equatorialPlane: MeshRenderer = null;
    @property(MeshRenderer) public eclipticPlane: MeshRenderer = null;

    public reorientate(moon?: Dsm): void {
        this._moon = moon ?? null;
        this.equatorialPlane.node.forward = planetary.center.rotator.right;
    }

    protected onLoad(): void {
        [this.lunarPlane, this.equatorialPlane, this.eclipticPlane].forEach(plane => {
            const pass0 = plane.material.passes[0];
            const handle = pass0.getHandle('rolling');
            this._planes.set(plane.node, { pass0, handle, rolling: 0 });
        });
    }

    public checkLoadTex(): void {
        if (!Astronomical.LoadTexTaskLaunched) {
            Astronomical.LoadTexTaskLaunched = true;
            resources.load(Res.Textures.AST_PLANES, Texture2D, (err, tex) => {
                if (err) console.error('planes texture', err);
                else {
                    [this.lunarPlane, this.equatorialPlane, this.eclipticPlane].forEach(plane =>
                        plane.material.setProperty('mainTexture', tex));
                }
            });
        }
    }

    protected onEnable(): void { this._syncToCenter(); }

    protected update(dt: time.sec): void {
        this._syncToCenter();
        this._syncLunar();
        this._record();
        for (const [plane,] of this._planes) {
            plane.active = planetary['show' + plane.name] as boolean;
            if (plane.active && !records.get(plane)) this._planes.get(plane).rolling = 1;
        };
    }

    private _syncToCenter(): void {
        const { center } = planetary;
        if (center) {
            this.node.setWorldScale(Vec3.multiplyScalar(v3a, center.node.worldScale, PLANE_RATIO));
            this.node.setWorldPosition(center.node.worldPosition);
        }
    }

    private _syncLunar(): void {
        if (this._moon && planetary.showLunarPlane) {
            if (timer.timescale < 1 && this._lunarInited) return;
            const { center: planet, center: { belong: sun } } = planetary, moon = this._moon;
            const rad = Math.parseRadian(moon.longitudeOfNode);
            v3a.set(Math.sin(rad), 0, Math.cos(rad));
            Vec3.subtract(v3b, moon.node.worldPosition, planet.node.worldPosition);
            Vec3.cross(v3a, v3a, v3b);  // v3a = normal(up)
            if (v3a.y < 0) v3a.negative();
            Vec3.subtract(v3b, sun.node.worldPosition, planet.node.worldPosition);
            Vec3.cross(v3b, v3a, v3b);  // v3b = right
            // Vec3.cross(v3b, v3b, v3a);  // v3b = forward
            this.lunarPlane.node.lookAt(v3b, v3a);
            this._lunarInited = true;
        }
    }

    protected lateUpdate(dt: time.sec): void {
        for (const [plane, { rolling }] of this._planes) {
            const rec = this._planes.get(plane);
            if (rolling > 0) rec.rolling -= dt; else rec.rolling = 0;
            plane.active && rec.pass0.setUniform(rec.handle, easing.sineIn(rec.rolling));
        };
    }

    private _record(): void {
        this.node.children.forEach(child => {
            records.set(child, child.active);
        });
    }

    private _moon: Dsm = null;
    private _lunarInited: boolean = false;
    private _planes: Map<Node, {
        pass0: renderer.Pass,
        handle: handle,
        rolling: number,
    }> = new Map();

    public static LoadTexTaskLaunched: boolean = false;
}

const records: Map<Node, boolean> = new Map;
const v3a = new Vec3;
const v3b = new Vec3;
const PLANE_RATIO = 2;